package cn.zk.curator;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

/**
 * ClassName: CuratorFirst
 * Desc: 同步操作用法
 * Date： 2016/12/5
 * Created：shaom
 */
public class CuratorFirst {

    public static final String connectString = "master:2181,slave01:2181,slave02:2181";
    public static final String path = "/Curator";
    public static final String value = "CuratorValue";

    CuratorFramework client = CuratorFrameworkFactory.builder()
            .connectString(connectString)
            .sessionTimeoutMs(5000)
            .retryPolicy(new ExponentialBackoffRetry(2000, 5))
            .build();

    /**
     * 创建客户端
     * RetryPolicy：重试策略
     */
    void createClient() {
        CuratorFramework client = CuratorFrameworkFactory.newClient(connectString, 5000, 2000, new ExponentialBackoffRetry(2000, 5));
        client.start();

        // Fluent风格
        client = CuratorFrameworkFactory.builder().connectString(connectString)
                .sessionTimeoutMs(5000)
                .retryPolicy(new ExponentialBackoffRetry(2000, 5))
                .build();
        client.start();

        // 创建隔离命名空间的会话
        // 为了业务隔离，为每个业务分配一个独立命名空间，即指定一个zk根路径。
        client = CuratorFrameworkFactory.builder().connectString(connectString)
                .sessionTimeoutMs(5000)
                .retryPolicy(new ExponentialBackoffRetry(2000, 5))
                .namespace("base")
                .build();
        client.start();


    }

    /**
     * 新增节点
     *
     * @throws Exception
     */
    void createNode() throws Exception {
        client.start();

        // 创建一个节点，初始化内容为空；默认是持久节点
        client.create().forPath(path);

        // 创建节点，附件内容
        client.create().forPath(path + 1, value.getBytes());

        // 创建临时节点
        client.create().withMode(CreateMode.EPHEMERAL).forPath(path + 2);

        // 创建节点，并自动递归创建父节点
        // zk中NoNodeException ,使用 creatingParentsIfNeeded避免
        // 注意：非叶子节点都是持久节点
        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path + 3, value.getBytes());

        //
//        client.create().creatingParentContainersIfNeeded().forPath(path);
    }

    /**
     * 删除节点
     *
     * @throws Exception
     */
    void delNode() throws Exception {
        client.start();

        // 删除一个节点，只能删除叶子节点
        client.delete().forPath(path);

        // 递归删除
        client.delete().deletingChildrenIfNeeded().forPath(path);

        // 删除指定版本
        client.delete().withVersion(-1).forPath(path);

        // 保证强制删除
        // guaranteed()接口是一个保障措施，只要客户端会话有效，那么
        // Curator 会在后台持续进行删除操作，知道节点删除成功
        client.delete().guaranteed().forPath(path);

    }

    /**
     * 获取节点数据
     *
     * @throws Exception
     */
    void getData() throws Exception {
        client.start();
        // 读取一个节点的值
        byte[] bytes = client.getData().forPath(path);

        // 读取一个节点的数据内容，同时获取改节点的stat
        // 需要传入一个旧的stat，新的保存在这个旧的中
        Stat stat = new Stat();
        client.getData().storingStatIn(stat).forPath(path);
    }

    /**
     * 更新数据
     *
     * @throws Exception
     */
    void updateData() throws Exception {
        client.start();

        // 更新一个节点数据，返回一个stat对象
        Stat stat = client.setData().forPath(path);

        // 更新一个节点的数据内容，强制指定版本进行更新
        // withVersion 实现CAS
        client.setData().withVersion(1).forPath(path);
    }

}
